package com.example.oauth2.config;

import com.example.oauth2.enums.CustomScopes;
import com.example.oauth2.utils.TokenClaimsUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;

import java.time.Duration;
import java.util.List;
import java.util.Set;

/**
 * @author zty
 * @apiNote 密码加密类
 * @user nav1c
 * @date 2023/12/9 18:55
 * @project study-cloud
 */
@Configuration
public class OtherConfig {

    /**
     * token生成器
     */
    @Bean
    public OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator() {
        OAuth2AccessTokenGenerator oAuth2AccessTokenGenerator = new OAuth2AccessTokenGenerator();
        oAuth2AccessTokenGenerator.setAccessTokenCustomizer(context -> context.getClaims()
                .claims(TokenClaimsUtils.customizerUserAuthorities(context.getAuthorizedScopes(), context.getPrincipal())));
        OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
        return new DelegatingOAuth2TokenGenerator(oAuth2AccessTokenGenerator, refreshTokenGenerator);
    }

    /**
     * 密码加密器
     */
    @Bean
    @Primary
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 授权同意服务
     */
    @Bean
    public OAuth2AuthorizationConsentService inMemoryOAuth2AuthorizationConsentService(){
        return new InMemoryOAuth2AuthorizationConsentService();
    }

    /**
     * 授权服务
     */
    @Bean
    public OAuth2AuthorizationService inMemoryOAuth2AuthorizationService(){
        return new InMemoryOAuth2AuthorizationService();
    }

    /**
     * 用户管理器
     */
    @Bean
    public UserDetailsManager inMemoryUserDetailsManager() {
        return new InMemoryUserDetailsManager(administrator());
    }

    /**
     * 客户端存储器
     */
    @Bean
    public RegisteredClientRepository inMemoryRegisteredClientRepository(){
        return new InMemoryRegisteredClientRepository(List.of(fisrtRegisteredClient()));
    }

    private UserDetails administrator(){
        return User.withUsername("admin")
                .passwordEncoder(passwordEncoder()::encode)
                .password("123")
//                .roles(roles())
                .authorities("user:add")
                .accountExpired(false)
                .accountLocked(false)
                .credentialsExpired(false)
                .disabled(false)
                .build();
    }

    private RegisteredClient fisrtRegisteredClient(){
        return RegisteredClient.withId("my_client")
                .clientId("my_client")
                .clientName("my_client")
                .clientSecret(passwordEncoder().encode("123"))//要加密 否则 ClientSecretAuthenticationProvider.authenticate报错
                .redirectUris(strings ->
                        strings.add("https://www.baidu.com"))
                .authorizationGrantTypes(
                        authorizationGrantTypes ->
                                authorizationGrantTypes.addAll(
                                        Set.of(AuthorizationGrantType.AUTHORIZATION_CODE,
                                                AuthorizationGrantType.REFRESH_TOKEN,
                                                AuthorizationGrantType.CLIENT_CREDENTIALS,
                                                AuthorizationGrantType.PASSWORD,
                                                AuthorizationGrantType.DEVICE_CODE,
                                                AuthorizationGrantType.JWT_BEARER)))
                .clientAuthenticationMethods(clientAuthenticationMethods ->
                        clientAuthenticationMethods.addAll(
                                Set.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
                                        ClientAuthenticationMethod.CLIENT_SECRET_POST,
                                        ClientAuthenticationMethod.CLIENT_SECRET_JWT,
                                        ClientAuthenticationMethod.PRIVATE_KEY_JWT,
                                        ClientAuthenticationMethod.NONE)))
                .scopes(strings -> strings.addAll(Set.of("read", "write", CustomScopes.USER_AUTHORITY)))
                .clientSettings(ClientSettings.builder()
                        //是否需要同意授权
                        //OAuth2AuthorizationConsentAuthenticationProvider 的逻辑有问题 必然报错
                        //不对，如果访问授权地址时没有带上scope=xxx时才会报错
                        .requireAuthorizationConsent(true)
                        //用于从授权服务器检索 JSON Web Key (JWK) 集的URI，其中包含用于验证ID令牌的 JSON Web Signature (JWS) 和（可选）用户信息响应的加密密钥。
                        .jwkSetUrl("https://www.baidu.com")
                        .build())
                .tokenSettings(TokenSettings.builder()
                        .accessTokenTimeToLive(Duration.ofHours(1))
                        .reuseRefreshTokens(true)//是否重用刷新token false 会重新生成刷新token
                        //用OAuth2TokenFormat.SELF_CONTAINED 无法在OAuth2AccessTokenGenerator#generate(OAuth2TokenContext context)中生成token?
                        .accessTokenFormat(OAuth2TokenFormat.REFERENCE)
                        .build())
                .build();
    }

}
